
#include "comm.h"

CommPort::CommPort(int portnumber, int baudrate)
{
	ZeroMemory(&original_timeouts, sizeof(COMMTIMEOUTS));
	ZeroMemory(&device_configuration, sizeof(DCB));
	ZeroMemory(&overlapped_read, sizeof(OVERLAPPED));
	ZeroMemory(&overlapped_write, sizeof(OVERLAPPED));

	handle = NULL;
	port_number = portnumber;
	baud_rate = baudrate;
	device_configuration.ByteSize = sizeof(device_configuration);
	device_configuration.BaudRate = baudrate;
	device_configuration.fBinary = 1;
	device_configuration.fParity = 0;
	device_configuration.fOutxCtsFlow = 0;
	device_configuration.fOutxDsrFlow = 0;
	device_configuration.fDtrControl = DTR_CONTROL_ENABLE;
	device_configuration.fDsrSensitivity = 0;
	device_configuration.fTXContinueOnXoff = 1;
	device_configuration.fOutX = 0;
	device_configuration.fInX = 0;
	device_configuration.fErrorChar = 0;
	device_configuration.fNull = 0;
	device_configuration.fRtsControl = RTS_CONTROL_ENABLE;
	device_configuration.fAbortOnError = 0;
	device_configuration.wReserved = 0;
	device_configuration.XonLim = 1;
	device_configuration.XoffLim = 2;
	device_configuration.ByteSize = 8;
	device_configuration.Parity = NOPARITY;
	device_configuration.StopBits = ONESTOPBIT;
	if ( _open() == false ) throw "port error";
}

bool CommPort::_open()
{
	// parse port name ..
	char buffer[20];
	BOOL status = 0;
	std::string portname;
	if (port_number > 8) portname = "\\\\.\\COM";
	else portname = "COM";
	_itoa(port_number, buffer, 10);
	portname += buffer;

	// open file:
	HANDLE commport = CreateFile(
		portname.c_str(),
		GENERIC_READ | GENERIC_WRITE,
		0, // exclusive access
		NULL,
		OPEN_EXISTING,
		FILE_FLAG_OVERLAPPED | FILE_FLAG_WRITE_THROUGH,
		NULL
		);
	if (commport == INVALID_HANDLE_VALUE) {
		log( "cannot open file handle ." );
		return false;
	}

	// configure ..
	SetupComm(commport, 64, 64);
	GetCommTimeouts(commport, &original_timeouts);
	status = SetCommState(commport, &device_configuration);
	if (status == 0) {
		log( "cannot set comm state ." );
		return false;
	}
	handle = commport;

	PurgeComm(handle, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR );
	overlapped_read.Offset = overlapped_read.OffsetHigh = 0;
	overlapped_write.Offset = overlapped_write.OffsetHigh = 0;
	overlapped_write.hEvent = CreateEvent(NULL, 0, 0, NULL);
	overlapped_read.hEvent = CreateEvent(NULL, 1, 0, NULL);

	return true;
}

void CommPort::setRTS(bool state)
{	// Set terminal status line: Request To Send
	if (state) EscapeCommFunction(handle, SETRTS);
	else EscapeCommFunction(handle, CLRRTS);
}

void CommPort::setDTR(bool state)
{	// Set terminal status line: Data Terminal Ready
	if (state) EscapeCommFunction(handle, SETDTR);
	else EscapeCommFunction(handle, CLRDTR);
}

bool CommPort::getCTS()
{	// Read terminal status line: Clear To Send
	DWORD status = 0;
	GetCommModemStatus(handle, &status);
	return MS_CTS_ON & status;
}
bool CommPort::getDSR()
{	// Read terminal status line: Data Set Ready
	DWORD status = 0;
	GetCommModemStatus(handle, &status);
	return MS_DSR_ON & status;
}
bool CommPort::getRI()
{	// Read terminal status line: Ring Indicator
	DWORD status = 0;
	GetCommModemStatus(handle, &status);
	return MS_RING_ON & status;
}
bool CommPort::getCD()
{	// Read terminal status line: Carrier Detect
	DWORD status = 0;
	GetCommModemStatus(handle, &status);
	return MS_RLSD_ON & status;
}

void CommPort::write( std::string & data )
{
	DWORD write_len = 0, completed = 0;
	WriteFile(handle, data.c_str(), data.size(), &write_len, &overlapped_write);
	GetOverlappedResult(handle, &overlapped_write, &completed, 1);
}

std::string CommPort::read( int len )
{
	char * read_buffer = new char[len+1];
	DWORD errors = 0, read_len = 0;
	COMSTAT status;
	ResetEvent(overlapped_read.hEvent);
	ClearCommError(handle, &errors, &status);
	// now can check status ..

	ReadFile(handle, read_buffer, len, &read_len, &overlapped_read);
	read_buffer[read_len] = 0;
	WaitForSingleObject(overlapped_read.hEvent, INFINITE);

	std::string ret(read_buffer);
	delete [] read_buffer;
	return ret;
}

void CommPort::close()
{
	if (handle)
	{
		// Restore original timeout values:
		SetCommTimeouts(handle, &original_timeouts);
		CloseHandle(handle);
		handle = NULL;
	}
}

